package futbol.tacticas.concursantes2007.CabezudachoFC;

import futbol.General;
import futbol.tacticas.*;
import futbol.tacticas.concursantes.Tactica;

import java.awt.*;
import java.util.*;

/*
 * El portero se queda en la porteria siguiendo el balon desde un palo hacia el
 * otro excepto cuando el balon entra en la porteria, en cuyo caso intenta 
 * colocarse donde va a ir el balon para despejar lateralmente.
 */
public class TacticaCabezudachoFC implements Tactica {
  private static final Point[] POSICIONES_INICIALES = 
                                         { new Point(-150,    0),   //  1
                                           new Point(-100,  -60),   //  2 
                                           new Point(-100,    0),   //  3 
                                           new Point(-100,   60),   //  4 
                                           new Point( -30,  -70),   //  5 
                                           new Point( -30,  -30),   //  6 
                                           new Point( -30,   30),   //  7 
                                           new Point( -30,   70),   //  8 
                                           new Point(  30,  -90),   //  9 
                                           new Point(  30,   90),   // 10 
                                           new Point(  60,    0) }; // 11 
                                           
  private static final Point[] POSICIONES_SAQUE = 
                                     { new Point(-155,    0),   //  1
                                       new Point(-130,  -60),   //  2 
                                       new Point(-130,    0),   //  3 
                                       new Point(-130,   60),   //  4 
                                       new Point( -95,  -70),   //  5 
                                       new Point( -95,  -30),   //  6 
                                       new Point( -95,   30),   //  7 
                                       new Point( -95,   70),   //  8 
                                       new Point( -65,  -90),   //  9 
                                       new Point( -65,   90),   // 10 
                                       new Point( -50,    0) }; // 11 
                                       
  private static final int X_MIN = 0;                                     
  private static final int X_MAX = 1;                                     
  private static final int Y_MIN = 2;                                     
  private static final int Y_MAX = 3;                                     
                                       
  private static final int[][] ZONA =
                         { { -160, -110,  -70,   70 },    //  1
                           { -160,  -80, -120,  -40 },    //  2
                           { -160,  -80,  -40,   40 },    //  3
                           { -160,  -80,   40,  120 },    //  4
                           { -120,   40, -120,  -70 },    //  5
                           { -120,   60,  -90,    0 },    //  6
                           { -120,   60,    0,   90 },    //  7
                           { -120,   40,   70,  120 },    //  8
                           {   30,  160, -120,  -40 },    //  9
                           {   30,  160,   40,  120 },    // 10
                           {   50,  160,  -50,   50 } };  // 11
                                           
  private static final int[] FUERZA =
                         { 6,   //  1
                           7,   //  2
                           7,   //  3
                           7,   //  4
                           5,   //  5
                           4,   //  6
                           4,   //  7
                           5,   //  8
                           6,   //  9
                           6,   // 10
                           7 }; // 11
                           
  private static final int[] VELOCIDAD;
  
  private static final int PORTERO         =  0; 
  private static final int CENTRAL_IZQ     =  1; 
  private static final int LIBRE           =  2; 
  private static final int CENTRAL_DCH     =  3; 
  private static final int INTERIOR_IZQ    =  4; 
  private static final int MEDIOCENTRO_IZQ =  5; 
  private static final int MEDIOCENTRO_DCH =  6; 
  private static final int INTERIOR_DCH    =  7; 
  private static final int EXTREMO_IZQ     =  8; 
  private static final int EXTREMO_DCH     =  9; 
  private static final int DELANTERO       = 10; 
  
  private static final int[] CENTRALES = { CENTRAL_IZQ, LIBRE, CENTRAL_DCH };
  private static final int[] MEDIOCENTROS = { MEDIOCENTRO_IZQ, MEDIOCENTRO_DCH };
  private static final int[] INTERIORES = { INTERIOR_IZQ, INTERIOR_DCH };
  private static final int[] EXTREMOS = { EXTREMO_IZQ, EXTREMO_DCH };
  
  private static final int[] JUG_RUTA = { -1, -1, -1, -1, 0, -1, -1, 1, 2, 3, 4 };
  private static final Point[][] RUTA = { { new Point(-100,  -95),
                                            new Point( -50,  -80),
                                            new Point( -10, -110),
                                            new Point(  40,  -95) }, // INTERIOR_IZQ
                                          { new Point(-100,   95),
                                            new Point( -50,   80),
                                            new Point( -10,  110),
                                            new Point(  40,   95) }, // INTERIOR_DCH
                                          { new Point(  40, -110),
                                            new Point( 110,  -50),
                                            new Point(  40,  -50) }, // EXTREMO_IZQ
                                          { new Point(  40,  110),
                                            new Point( 110,   50),
                                            new Point(  40,   50) }, // EXTREMO_DCH
                                          { new Point(  60,    0),
                                            new Point( 130,  -40),
                                            new Point( 130,  -20),
                                            new Point( 100,    0),
                                            new Point( 130,   20),
                                            new Point( 130,   40),
                                            new Point(  60,    0),
                                            new Point( 130,   40),
                                            new Point( 130,   20),
                                            new Point( 100,    0),
                                            new Point( 130,  -20),
                                            new Point( 130,  -40) } }; // DELANTERO
  private static final int[] TAM_RUTA;
  
  private static final Point[] POS_PALO = { new Point(160, -25),
                                            new Point(160,  25) };
                                          
  private static Random aleatorios;
  
  private static int[] posRuta;
  
  private Point[] jugadoresAnt;
  private Point bolaAnt;
  
  static {
    VELOCIDAD = new int[11];
    
    aleatorios = new Random(System.currentTimeMillis());
    
    int numRutas = RUTA.length;
    TAM_RUTA = new int[numRutas];
    posRuta = new int[numRutas];
    for (int i = 0; i < numRutas; i++) {
      TAM_RUTA[i] = RUTA[i].length;
      posRuta[i] = 0;
    }
  }
  
  public TacticaCabezudachoFC() {
    jugadoresAnt = new Point[11];
    
    for (int i = 0; i < 11; i++) {
      VELOCIDAD[i] = 10 - FUERZA[i];
      jugadoresAnt[i] = POSICIONES_SAQUE[i];
    }
    
    bolaAnt = new Point(0, 0);
  }
  
  public String getNombre() {
    return ("Cabezudacho F. C.");
  }
  
  public Color getColor1() {
    return (Color.GREEN);
  }
    
  public Color getColor2() {
    return (Color.BLACK);
  }
    
  public Color getColorNumero() {
    return (Color.WHITE);
  }
  
  public int getFuerza(int indJugador) {
    return (FUERZA[indJugador]);
  }
    
  public int getXInicial(int indJugador) {
    return (POSICIONES_INICIALES[indJugador].x);
  }
    
  public int getYInicial(int indJugador) {
    return (POSICIONES_INICIALES[indJugador].y);
  }
  
  public Vector<Comando> getComandos(SituacionJugadores situacion) {
    Vector<Comando> comandos = new Vector();
    
    Point[] jugadores = situacion.getMisJugadores();
    Point[] contrarios = situacion.getContrario();
    Point posicionBola = situacion.getBola();
    Point bolaSig = calculaSigPosicion(bolaAnt, posicionBola);
    
    portero(comandos, jugadores[PORTERO], posicionBola, bolaSig);

    int numCentrales = CENTRALES.length;
    for (int c = 0; c < numCentrales; c++) {
      int central = CENTRALES[c];
      
      central(comandos, central, jugadores, posicionBola, bolaSig, contrarios);
    }
    
    int numMediocentros = MEDIOCENTROS.length;
    for (int m = 0; m < numMediocentros; m++) {
      int mediocentro = MEDIOCENTROS[m];
      
      mediocentro(comandos, mediocentro, jugadores, posicionBola, bolaSig, contrarios);
    }
    
    int numInteriores = INTERIORES.length;
    for (int i = 0; i < numInteriores; i++) {
      int interior = INTERIORES[i];
      
      interior(comandos, interior, jugadores, posicionBola, bolaSig, contrarios);
    }
    
    int numExtremos = EXTREMOS.length;
    for (int i = 0; i < numExtremos; i++) {
      int extremo = EXTREMOS[i];
      
      extremo(comandos, extremo, jugadores, posicionBola, bolaSig, contrarios);
    }
    
    delantero(comandos, DELANTERO, jugadores[DELANTERO], posicionBola, bolaSig);
    
    bolaAnt = posicionBola;
    
    return (comandos);
  }
  
  private void portero(Vector<Comando> comandos, 
                       Point posicionPortero, 
                       Point posicionBola,
                       Point bolaSig) {
    // MOVIMIENTO
    // Si el balon esta o estara en la siguiente jugada dentro del area va hacia 
    // el balon y sino vuelve al sitio ajustando en la vertical de la porteria 
    // en funcion de la posicion del balon.
    if ((estaEnZona(PORTERO, posicionBola)) || (estaEnZona(PORTERO, bolaSig))) {
      if (llegaAPunto(posicionPortero, posicionBola, VELOCIDAD[PORTERO])) {
        comandos.add(new ComandoIrA(PORTERO, posicionBola.x, posicionBola.y));
      } else if (llegaAPunto(posicionPortero, bolaSig, VELOCIDAD[PORTERO])) {
        comandos.add(new ComandoIrA(PORTERO, bolaSig.x, bolaSig.y));
      } else {
        Point bolaSig2 = calculaSigPosicion(posicionBola, bolaSig);
        
        comandos.add(new ComandoIrA(PORTERO, bolaSig2.x, bolaSig2.y));
      }
    } else {
      Point posicionInicial = POSICIONES_INICIALES[PORTERO];
      if (bolaSig.y > 30) {
        comandos.add(new ComandoIrA(PORTERO, posicionInicial.x, 30));
      } else if (bolaSig.y < -30) {
        comandos.add(new ComandoIrA(PORTERO, posicionInicial.x, -30));
      } else {
        comandos.add(new ComandoIrA(PORTERO, posicionInicial.x, bolaSig.y));
      }
    }
    
    // DESPEJE
    // Si el portero esta en la perpendicular del area envia el balon hacia 
    // adelante.
    // En otro caso envia el balon hacia el lateral mas cercano al portero.
    if ((posicionPortero.y < 60) && (posicionPortero.y > -60)) {
      comandos.add(new ComandoGolpearBola(PORTERO, posicionPortero.x + 50,
                                          posicionPortero.y));
    } else if (posicionPortero.y > 0) {
      comandos.add(new ComandoGolpearBola(PORTERO, posicionPortero.x,
                                          posicionPortero.y + 50));
    } else { // posicionPortero.y < 0
      comandos.add(new ComandoGolpearBola(PORTERO, posicionPortero.x,
                                          posicionPortero.y - 50));
    }
  }
  
  private void central(Vector<Comando> comandos, 
                       int central,
                       Point[] jugadores, 
                       Point posicionBola,
                       Point bolaSig,
                       Point[] contrarios) {
    // MOVIMIENTO
    // Si el balon esta o estara en la siguiente jugada en su zona del campo va 
    // a por el balon.
    // Si no esta el balon, pero hay cerca un contrario va a marcarlo quedando 
    // mas cerca de la porteria que el contrario.
    // En otro caso va hacia su posicion inicial basculando hacia el balon.
    Point posicionCentral = jugadores[central];
    if ((estaEnZona(central, posicionBola)) || (estaEnZona(central, bolaSig))) {
      if (llegaAPunto(posicionCentral, posicionBola, VELOCIDAD[central])) {
        comandos.add(new ComandoIrA(central, posicionBola.x, posicionBola.y));
      } else if (llegaAPunto(posicionCentral, bolaSig, VELOCIDAD[central])) {
        comandos.add(new ComandoIrA(central, bolaSig.x, bolaSig.y));
      } else {
        Point bolaSig2 = calculaSigPosicion(posicionBola, bolaSig);
        
        comandos.add(new ComandoIrA(central, bolaSig2.x, bolaSig2.y));
      }
    } else {
      int contrario = contrarioEnZona(central, contrarios);
      if (contrario == -1) { // Va a su posicion basculando.
        Point posicionInicial = POSICIONES_INICIALES[central];
        int xBasculando;
        if (posicionBola.x < ZONA[central][X_MAX]) {
          xBasculando = posicionBola.x;
        } else {
          xBasculando = ZONA[central][X_MAX];
        }
        
        int yBasculando;
        if (posicionInicial.y == posicionBola.y) {
          yBasculando = posicionBola.y;
        } if (posicionInicial.y < posicionBola.y) {
          yBasculando = 40 * (posicionBola.y - posicionInicial.y) / 
                        (120 - posicionInicial.y);
          yBasculando += posicionInicial.y; 
        } else { // posicionInicial.y > posicionBola.y
          yBasculando = 40 * (120 + posicionBola.y) / (120 + posicionInicial.y);
          yBasculando += posicionInicial.y - 40; 
        }
        
        comandos.add(new ComandoIrA(central, xBasculando, yBasculando));
      } else { // Sigue al contario en su zona mas proximo a la porteria.
        Point posicionContrario = contrarios[contrario];
        
        comandos.add(new ComandoIrA(central, posicionContrario.x - 5, 
                                    posicionContrario.y));
      }
    }
    
    // PASES
    // Si uno de sus apoyos esta disponible se le pasa.
    // En otro caso envia el balon hacia adelante con angulo aleatorio entre 
    // -30 y 30.
    int[] apoyos= new int[2];
    switch (central) {
      case CENTRAL_DCH:
        apoyos[0] = INTERIOR_DCH;
        apoyos[1] = MEDIOCENTRO_DCH;
        break;
      case CENTRAL_IZQ:
        apoyos[0] = INTERIOR_IZQ;
        apoyos[1] = MEDIOCENTRO_IZQ;
        break;
      default: // LIBRE
        apoyos[0] = MEDIOCENTRO_DCH;
        apoyos[1] = MEDIOCENTRO_IZQ;
        break;
    }
    
    boolean despejar = true;
    for (int i = 0; despejar && (i < 2); i++) {
      double dist = distanciaARecta(posicionBola, 
                                    jugadores[apoyos[i]], 
                                    contrarios[0]);
      for (int j = 1; j < 11; j++) {
        double dist2 = distanciaARecta(posicionBola, 
                                       jugadores[apoyos[i]], 
                                       contrarios[j]);
        if (dist2 < dist) {
          dist = dist2;
        }
      }
      
      if (dist > 15) {
        despejar = false;

        comandos.add(new ComandoPase(central, apoyos[i]));
      }
    }
    
    if (despejar) {
      double anguloDespeje = Math.toRadians(aleatorios.nextInt(60) - 30);
      
      int incrX = (int) (Math.cos(anguloDespeje) * 100);
      int incrY = (int) (Math.sin(anguloDespeje) * 100);
      
      comandos.add(new ComandoGolpearBola(central, posicionCentral.x + incrX,
                                          posicionCentral.y + incrY));
    }
  }
  
  private void mediocentro(Vector<Comando> comandos, 
                           int mediocentro,
                           Point[] jugadores, 
                           Point posicionBola,
                           Point bolaSig,
                           Point[] contrarios) {
    // MOVIMIENTO
    // Si el balon esta o estara en la siguiente jugada en su zona del campo va 
    // a por el balon.
    // En otro caso va hacia su posicion inicial basculando hacia el balon.
    Point posicionMediocentro = jugadores[mediocentro];
    if ((estaEnZona(mediocentro, posicionBola)) || (estaEnZona(mediocentro, bolaSig))) {
      if (llegaAPunto(posicionMediocentro, posicionBola, VELOCIDAD[mediocentro])) {
        comandos.add(new ComandoIrA(mediocentro, posicionBola.x, posicionBola.y));
      } else if (llegaAPunto(posicionMediocentro, bolaSig, VELOCIDAD[mediocentro])) {
        comandos.add(new ComandoIrA(mediocentro, bolaSig.x, bolaSig.y));
      } else {
        Point bolaSig2 = calculaSigPosicion(posicionBola, bolaSig);
        
        comandos.add(new ComandoIrA(mediocentro, bolaSig2.x, bolaSig2.y));
      }
    } else {
      Point posicionInicial = POSICIONES_INICIALES[mediocentro];
      int xBasculando;
      if (posicionBola.x < ZONA[mediocentro][X_MIN]) {
        xBasculando = ZONA[mediocentro][X_MIN];
      } else if (posicionBola.x > ZONA[mediocentro][X_MAX]) {
        xBasculando = ZONA[mediocentro][X_MAX];
      } else {
        xBasculando = posicionBola.x;
      }

      int yBasculando;
      if (posicionInicial.y == posicionBola.y) {
        yBasculando = posicionBola.y;
      } if (posicionInicial.y < posicionBola.y) {
        yBasculando = (ZONA[mediocentro][Y_MAX] - posicionInicial.y) *
                      (posicionBola.y - posicionInicial.y) / 
                      (120 - posicionInicial.y);
        yBasculando += posicionInicial.y; 
      } else { // posicionInicial.y < posicionBola.y
        yBasculando = (posicionInicial.y - ZONA[mediocentro][Y_MIN]) *
                      (posicionInicial.y - posicionBola.y) / 
                      (120 + posicionInicial.y);
        yBasculando += posicionInicial.y - 
                       (posicionInicial.y - ZONA[mediocentro][Y_MIN]); 
      }

      comandos.add(new ComandoIrA(mediocentro, xBasculando, yBasculando));
    }
    
    // PASES
    // Si uno de sus apoyos esta disponible se le pasa.
    // En otro caso envia el balon hacia adelante con angulo aleatorio entre 
    // -45 y 45.
    int[] apoyos= new int[2];
    switch (mediocentro) {
      case MEDIOCENTRO_DCH:
        apoyos[0] = DELANTERO;
        apoyos[1] = INTERIOR_DCH;
        break;
      default: // MEDIOCENTRO_IZQ
        apoyos[0] = DELANTERO;
        apoyos[1] = INTERIOR_IZQ;
        break;
    }
    
    boolean despejar = true;
    for (int i = 0; despejar && (i < 2); i++) {
      double dist = distanciaARecta(posicionBola, 
                                    jugadores[apoyos[i]], 
                                    contrarios[0]);
      for (int j = 1; j < 11; j++) {
        double dist2 = distanciaARecta(posicionBola, 
                                       jugadores[apoyos[i]], 
                                       contrarios[j]);
        if (dist2 < dist) {
          dist = dist2;
        }
      }
      
      if (dist > 15) {
        despejar = false;

        comandos.add(new ComandoPase(mediocentro, apoyos[i]));
      }
    }
    
    if (despejar) {
      double anguloDespeje = Math.toRadians(aleatorios.nextInt(90) - 45);

      int incrX = (int) (Math.cos(anguloDespeje) * 100);
      int incrY = (int) (Math.sin(anguloDespeje) * 100);
      
      comandos.add(new ComandoGolpearBola(mediocentro, posicionMediocentro.x + incrX,
                                          posicionMediocentro.y + incrY));
    }
  }
    
  private void interior(Vector<Comando> comandos, 
                       int interior,
                       Point[] jugadores, 
                       Point posicionBola,
                       Point bolaSig,
                       Point[] contrarios) {
    // MOVIMIENTO
    // Si el balon esta o estara en la siguiente jugada en su zona del campo va 
    // a por el balon.
    // Si no va hacia su posicion inicial basculando hacia el balon, mientras 
    // este por detras de la mitad de su zona o en su misma banda.
    // En otro caso realiza un movimiento fijo siguiendo varios puntos.                         
    Point posicionInterior = jugadores[interior];
    Point posicionInicial = POSICIONES_INICIALES[interior];
    if ((estaEnZona(interior, posicionBola)) || (estaEnZona(interior, bolaSig))) {
      if (llegaAPunto(posicionInterior, posicionBola, VELOCIDAD[interior])) {
        comandos.add(new ComandoIrA(interior, posicionBola.x, posicionBola.y));
      } else if (llegaAPunto(posicionInterior, bolaSig, VELOCIDAD[interior])) {
        comandos.add(new ComandoIrA(interior, bolaSig.x, bolaSig.y));
      } else {
        Point bolaSig2 = calculaSigPosicion(posicionBola, bolaSig);
        
        comandos.add(new ComandoIrA(interior, bolaSig2.x, bolaSig2.y));
      }
    } else if ((posicionBola.x > (ZONA[interior][X_MIN] + ((ZONA[interior][X_MAX] - ZONA[interior][X_MIN]) / 2))) ||
               (estaEnBandaContraria(interior, posicionBola))) {
      int posJugRuta = JUG_RUTA[interior];
      Point sigPuntoRuta = RUTA[posJugRuta][posRuta[posJugRuta]];
      if (posicionInterior.equals(sigPuntoRuta)) {
        int sigPosRuta = (posRuta[posJugRuta] + 1) % TAM_RUTA[posJugRuta];
        posRuta[posJugRuta] = sigPosRuta;
        sigPuntoRuta = RUTA[posJugRuta][sigPosRuta];
      }
      
      comandos.add(new ComandoIrA(interior, sigPuntoRuta.x, sigPuntoRuta.y));
    } else { // Balon detras del final de su zona o en su banda.
      int xBasculando;
      if (posicionBola.x < ZONA[interior][X_MIN]) {
        xBasculando = ZONA[interior][X_MIN];
      } else if (posicionBola.x > ZONA[interior][X_MAX]) {
        xBasculando = ZONA[interior][X_MAX];
      } else {
        xBasculando = posicionBola.x;
      }

      int yBasculando;
      if (posicionInicial.y == posicionBola.y) {
        yBasculando = posicionBola.y;
      } if (posicionInicial.y < posicionBola.y) {
        yBasculando = (ZONA[interior][Y_MAX] - posicionInicial.y) *
                      (posicionBola.y - posicionInicial.y) / 
                      (120 - posicionInicial.y);
        yBasculando += posicionInicial.y; 
      } else { // posicionInicial.y < posicionBola.y
        yBasculando = (posicionInicial.y - ZONA[interior][Y_MIN]) *
                      (posicionInicial.y - posicionBola.y) / 
                      (120 + posicionInicial.y);
        yBasculando += posicionInicial.y - 
                       (posicionInicial.y - ZONA[interior][Y_MIN]); 
      }

      comandos.add(new ComandoIrA(interior, xBasculando, yBasculando));
    }
    
    // PASES
    // Si uno de sus apoyos esta disponible se le pasa.
    // En otro caso envia el balon hacia adelante con angulo aleatorio entre 
    // -60 y 60.
    int[] apoyos= new int[2];
    switch (interior) {
      case INTERIOR_DCH:
        apoyos[0] = DELANTERO;
        apoyos[1] = EXTREMO_DCH;
        break;
      default: // INTERIOR_IZQ
        apoyos[0] = DELANTERO;
        apoyos[1] = EXTREMO_IZQ;
        break;
    }
    
    boolean despejar = true;
    for (int i = 0; despejar && (i < 2); i++) {
      double dist = distanciaARecta(posicionBola, 
                                    jugadores[apoyos[i]], 
                                    contrarios[0]);
      for (int j = 1; j < 11; j++) {
        double dist2 = distanciaARecta(posicionBola, 
                                       jugadores[apoyos[i]], 
                                       contrarios[j]);
        if (dist2 < dist) {
          dist = dist2;
        }
      }
      
      if (dist > 15) {
        despejar = false;

        comandos.add(new ComandoPase(interior, apoyos[i]));
      }
    }
    
    if (despejar) {
      double anguloDespeje = Math.toRadians(aleatorios.nextInt(120) - 60);

      int incrX = (int) (Math.cos(anguloDespeje) * 100);
      int incrY = (int) (Math.sin(anguloDespeje) * 100);
      
      comandos.add(new ComandoGolpearBola(interior, posicionInterior.x + incrX,
                                          posicionInterior.y + incrY));
    }
  }
    
  private void extremo(Vector<Comando> comandos, 
                       int extremo,
                       Point[] jugadores, 
                       Point posicionBola,
                       Point bolaSig,
                       Point[] contrarios) {
    // MOVIMIENTO
    // Si el balon esta o estara en la siguiente jugada en su zona del campo va 
    // a por el balon.
    // En otro caso realiza un movimiento fijo siguiendo varios puntos.                         
    Point posicionExtremo = jugadores[extremo];
    if ((estaEnZona(extremo, posicionBola)) || (estaEnZona(extremo, bolaSig))) {
      if (llegaAPunto(posicionExtremo, posicionBola, VELOCIDAD[extremo])) {
        comandos.add(new ComandoIrA(extremo, posicionBola.x, posicionBola.y));
      } else if (llegaAPunto(posicionExtremo, bolaSig, VELOCIDAD[extremo])) {
        comandos.add(new ComandoIrA(extremo, bolaSig.x, bolaSig.y));
      } else {
        Point bolaSig2 = calculaSigPosicion(posicionBola, bolaSig);
        
        comandos.add(new ComandoIrA(extremo, bolaSig2.x, bolaSig2.y));
      }
    } else {
      int posJugRuta = JUG_RUTA[extremo];
      Point sigPuntoRuta = RUTA[posJugRuta][posRuta[posJugRuta]];
      if (posicionExtremo.equals(sigPuntoRuta)) {
        int sigPosRuta = (posRuta[posJugRuta] + 1) % TAM_RUTA[posJugRuta];
        posRuta[posJugRuta] = sigPosRuta;
        sigPuntoRuta = RUTA[posJugRuta][sigPosRuta];
      }
      
      comandos.add(new ComandoIrA(extremo, sigPuntoRuta.x, sigPuntoRuta.y));
    }
    
    // TIRO
    // Tira a puerta hacia uno de los palos salvo que este lejos y tenga pase al
    // delantero.
    int palo = aleatorios.nextInt(2);
    Point posPalo = POS_PALO[palo];
    
    double distX = 160 - posicionExtremo.x;
    double distHipotenusa = General.getDistancia(posicionExtremo.x, 
                                                 posicionExtremo.y,
                                                 posPalo.x, posPalo.y);
    
    boolean tiro = true;
    if (distHipotenusa > 50) {
      double dist = distanciaARecta(posicionBola, 
                                    jugadores[DELANTERO], 
                                    contrarios[0]);
      for (int j = 1; j < 11; j++) {
        double dist2 = distanciaARecta(posicionBola, 
                                       jugadores[DELANTERO], 
                                       contrarios[j]);
        if (dist2 < dist) {
          dist = dist2;
        }
      }
      
      if (dist < 15) {
        tiro = false;

        comandos.add(new ComandoPase(extremo, DELANTERO));
      }
    }
    
    if (tiro) {
      int angulo = (int) (distX / distHipotenusa);
    
      if (posicionExtremo.y < posPalo.y) {
        angulo = -angulo;
      }

      comandos.add(new ComandoTiroAPuerta(extremo, angulo));
    } else {
      
    }
  }

  private void delantero(Vector<Comando> comandos, 
                       int delantero,
                       Point posicionDelantero, 
                       Point posicionBola,
                       Point bolaSig) {
    // MOVIMIENTO
    // Si el balon esta o estara en la siguiente jugada en su zona del campo va 
    // a por el balon.
    // En otro caso realiza un movimiento fijo siguiendo varios puntos.                         
    if ((estaEnZona(delantero, posicionBola)) || (estaEnZona(delantero, bolaSig))) {
      if (llegaAPunto(posicionDelantero, posicionBola, VELOCIDAD[delantero])) {
        comandos.add(new ComandoIrA(delantero, posicionBola.x, posicionBola.y));
      } else if (llegaAPunto(posicionDelantero, bolaSig, VELOCIDAD[delantero])) {
        comandos.add(new ComandoIrA(delantero, bolaSig.x, bolaSig.y));
      } else {
        Point bolaSig2 = calculaSigPosicion(posicionBola, bolaSig);
        
        comandos.add(new ComandoIrA(delantero, bolaSig2.x, bolaSig2.y));
      }
    } else {
      int posJugRuta = JUG_RUTA[delantero];
      Point sigPuntoRuta = RUTA[posJugRuta][posRuta[posJugRuta]];
      if (posicionDelantero.equals(sigPuntoRuta)) {
        int sigPosRuta = (posRuta[posJugRuta] + 1) % TAM_RUTA[posJugRuta];
        posRuta[posJugRuta] = sigPosRuta;
        sigPuntoRuta = RUTA[posJugRuta][sigPosRuta];
      }
      
      comandos.add(new ComandoIrA(delantero, sigPuntoRuta.x, sigPuntoRuta.y));
    }
    
    // TIRO
    // Tira a puerta hacia uno de los palos.
    int palo = aleatorios.nextInt(2);
    Point posPalo = POS_PALO[palo];
    
    double distX = 160 - posicionDelantero.x;
    double distHipotenusa = General.getDistancia(posicionDelantero.x, 
                                                 posicionDelantero.y,
                                                 posPalo.x, posPalo.y);
    
    int angulo = (int) (distX / distHipotenusa);
    
    if (posicionDelantero.y < posPalo.y) {
      angulo = -angulo;
    }
    
    comandos.add(new ComandoTiroAPuerta(delantero, angulo));
  }

  private Point calculaSigPosicion(Point pos1, Point pos2) {
    int incrX = pos2.x - pos1.x;
    int incrY = pos2.y - pos1.y;
    
    int x = pos2.x + incrX;
    int y = pos2.y + incrY;
    
    if (y > 120) {
      int dif = y - 120;
      
      y = 120 - dif;
    } else if (y < -120) {
      int dif = -120 - y;
      
      y = -120 + dif;
    }
    
    if (x > 160) {
      if ((y < 30) && (y > -30)) {
        x = 160;
      } else {
        int dif = x - 160;
      
        x = 160 - dif;
      }
    } else if (x < -160) {
      if ((y < 30) && (y > -30)) {
        x = -160;
      } else {
        int dif = -160 - x;
      
        x = -160 + dif;
      }
    }
    
    return (new Point(x, y));
  }
  
  private boolean llegaAPunto(Point pos1, Point pos2, int velocidad) {
    return (General.getDistancia(pos1.x, pos1.y, pos2.x, pos2.y) <= velocidad + 4);
  }
  
  private boolean estaEnZona(int jugador, Point pos) {
    return ((pos.x < ZONA[jugador][X_MAX]) && (pos.x > ZONA[jugador][X_MIN]) &&
            (pos.y < ZONA[jugador][Y_MAX]) && (pos.y > ZONA[jugador][Y_MIN]));
  }
  
  private int contrarioEnZona(int central, Point[] contrarios) {
    int contrario = -1;
    
    int xMin = 160;
    for (int i = 0; i < 11; i++) {
      Point posContrario = contrarios[i];
      if ((estaEnZona(central, posContrario)) && (xMin > posContrario.x)) {
        xMin = posContrario.x;
        contrario = i;
      }
    }
    
    return (contrario);
  }
  
  private boolean estaEnBandaContraria(int jugador, Point posicionBola) {
    int limiteBanda = ( (Math.abs(ZONA[jugador][Y_MAX]) > 
                         Math.abs(ZONA[jugador][Y_MIN])) ?
                        ZONA[jugador][Y_MAX] : ZONA[jugador][Y_MIN] );
    int limiteBandaContraria = -limiteBanda;
    
    int distBanda = Math.abs(limiteBanda - posicionBola.y);
    int distBandaContraria = Math.abs(limiteBandaContraria - posicionBola.y);
                   
    return (distBandaContraria < distBanda);
  }
  
  private double distanciaARecta(Point extremo1, Point extremo2, Point punto) {
    double distX1 = extremo2.x - extremo1.x;
    double distY1 = extremo2.y - extremo1.y;
    double distX2 = punto.x - extremo1.x;
    
    double distHipotenusa1 = Math.sqrt(Math.pow(distX1, 2) + Math.pow(distY1, 2));

    double distHipotenusa2 = (distHipotenusa1 * distX2) / distX1;
    
    double distY2 = Math.sqrt(Math.pow(distHipotenusa2, 2) - Math.pow(distX2, 2));
    
    double distHipotenusa3 = distY2 - (punto.y - extremo1.y);
    
    double distX3 = (distX1 * distHipotenusa3) / distHipotenusa1;
    
    return (distX3);
  }
}
